/******************************************************************************* * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.corext.refactoring.util; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Stack; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.LambdaExpression; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.ThrowStatement; import org.eclipse.jdt.core.dom.TryStatement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.UnionType; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.internal.corext.dom.Bindings; public abstract class AbstractExceptionAnalyzer extends ASTVisitor { private List<ITypeBinding> fCurrentExceptions; // Elements in this list are of type TypeBinding private Stack<List<ITypeBinding>> fTryStack; protected AbstractExceptionAnalyzer() { fTryStack= new Stack<List<ITypeBinding>>(); fCurrentExceptions= new ArrayList<ITypeBinding>(1); fTryStack.push(fCurrentExceptions); } @Override public abstract boolean visit(ThrowStatement node); @Override public abstract boolean visit(MethodInvocation node); @Override public abstract boolean visit(ClassInstanceCreation node); @Override public boolean visit(TypeDeclaration node) { // Don't dive into a local type. if (node.isLocalTypeDeclaration()) return false; return true; } @Override public boolean visit(EnumDeclaration node) { // Don't dive into a local type. if (node.isLocalTypeDeclaration()) return false; return true; } @Override public boolean visit(AnnotationTypeDeclaration node) { // Don't dive into a local type. if (node.isLocalTypeDeclaration()) return false; return true; } @Override public boolean visit(AnonymousClassDeclaration node) { // Don't dive into a local type. return false; } @Override public boolean visit(LambdaExpression node) { // Don't dive into a lambda type. return false; } @Override public boolean visit(TryStatement node) { fCurrentExceptions= new ArrayList<ITypeBinding>(1); fTryStack.push(fCurrentExceptions); // visit try block node.getBody().accept(this); List<VariableDeclarationExpression> resources= node.resources(); for (Iterator<VariableDeclarationExpression> iterator= resources.iterator(); iterator.hasNext();) { iterator.next().accept(this); } // Remove those exceptions that get catch by following catch blocks List<CatchClause> catchClauses= node.catchClauses(); if (!catchClauses.isEmpty()) handleCatchArguments(catchClauses); List<ITypeBinding> current= fTryStack.pop(); fCurrentExceptions= fTryStack.peek(); for (Iterator<ITypeBinding> iter= current.iterator(); iter.hasNext();) { addException(iter.next(), node.getAST()); } // visit catch and finally for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext(); ) { iter.next().accept(this); } if (node.getFinally() != null) node.getFinally().accept(this); // return false. We have visited the body by ourselves. return false; } @Override public boolean visit(VariableDeclarationExpression node) { if (node.getLocationInParent() == TryStatement.RESOURCES_PROPERTY) { Type type= node.getType(); ITypeBinding resourceTypeBinding= type.resolveBinding(); if (resourceTypeBinding != null) { IMethodBinding methodBinding= Bindings.findMethodInHierarchy(resourceTypeBinding, "close", new ITypeBinding[0]); //$NON-NLS-1$ if (methodBinding != null) { addExceptions(methodBinding.getExceptionTypes(), node.getAST()); } } } return super.visit(node); } protected void addExceptions(ITypeBinding[] exceptions, AST ast) { if(exceptions == null) return; for (int i= 0; i < exceptions.length;i++) { addException(exceptions[i], ast); } } protected void addException(ITypeBinding exception, AST ast) { exception= Bindings.normalizeForDeclarationUse(exception, ast); if (!fCurrentExceptions.contains(exception)) fCurrentExceptions.add(exception); } protected List<ITypeBinding> getCurrentExceptions() { return fCurrentExceptions; } private void handleCatchArguments(List<CatchClause> catchClauses) { for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext(); ) { Type type= iter.next().getException().getType(); if (type instanceof UnionType) { List<Type> types= ((UnionType) type).types(); for (Iterator<Type> iterator= types.iterator(); iterator.hasNext();) { removeCaughtExceptions(iterator.next().resolveBinding()); } } else { removeCaughtExceptions(type.resolveBinding()); } } } private void removeCaughtExceptions(ITypeBinding catchTypeBinding) { if (catchTypeBinding == null) return; for (Iterator<ITypeBinding> exceptions= new ArrayList<ITypeBinding>(fCurrentExceptions).iterator(); exceptions.hasNext();) { ITypeBinding throwTypeBinding= exceptions.next(); if (catches(catchTypeBinding, throwTypeBinding)) fCurrentExceptions.remove(throwTypeBinding); } } private boolean catches(ITypeBinding catchTypeBinding, ITypeBinding throwTypeBinding) { while(throwTypeBinding != null) { if (throwTypeBinding == catchTypeBinding) return true; throwTypeBinding= throwTypeBinding.getSuperclass(); } return false; } }